using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Reflection;

namespace DAL
{
    /// <summary>
    /// The base class for the <see cref="MainDB"/> class that 
    /// represents a connection to the <c>MainDB</c> database. 
    /// </summary>
    /// <remarks>
    /// Do not change this source code. Modify the MainDB class
    /// if you need to add or change some functionality.
    /// </remarks>
    public abstract class MainDB_Base : IDisposable
    {
        private IDbConnection _connection;
        private IDbTransaction _transaction;

        private StoredProcedures _storedProcedures;

        /// <summary>
        /// Initializes a new instance of the <see cref="MainDB_Base"/> 
        /// class and opens the database connection.
        /// </summary>
        protected MainDB_Base()
            : this(true)
        {
            // EMPTY
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="MainDB_Base"/> class.
        /// </summary>
        /// <param name="init">Specifies whether the constructor calls the
        /// <see cref="InitConnection"/> method to initialize the database connection.</param>
        protected MainDB_Base(bool init)
        {
            if (init)
                InitConnection();
        }

        /// <summary>
        /// Initializes the database connection.
        /// </summary>
        protected void InitConnection()
        {
            _connection = CreateConnection();
            _connection.Open();
        }

        /// <summary>
        /// Creates a new connection to the database.
        /// </summary>
        /// <returns>A reference to the <see cref="System.Data.IDbConnection"/> object.</returns>
        protected abstract IDbConnection CreateConnection();

        /// <summary>
        /// Returns a SQL statement parameter name that is specific for the data provider.
        /// For example it returns ? for OleDb provider, or @paramName for MS SQL provider.
        /// </summary>
        /// <param name="paramName">The data provider neutral SQL parameter name.</param>
        /// <returns>The SQL statement parameter name.</returns>
        protected internal abstract string CreateSqlParameterName(string paramName);

        /// <summary>
        /// Creates <see cref="System.Data.IDataReader"/> for the specified DB command.
        /// </summary>
        /// <param name="command">The <see cref="System.Data.IDbCommand"/> object.</param>
        /// <returns>A reference to the <see cref="System.Data.IDataReader"/> object.</returns>
        protected internal virtual IDataReader ExecuteReader(IDbCommand command)
        {
            return command.ExecuteReader();
        }

        /// <summary>
        /// Adds a new parameter to the specified command. It is not recommended that 
        /// you use this method directly from your custom code. Instead use the 
        /// <c>AddParameter</c> method of the &lt;TableCodeName&gt;Collection_Base classes.
        /// </summary>
        /// <param name="cmd">The <see cref="System.Data.IDbCommand"/> object to add the parameter to.</param>
        /// <param name="paramName">The name of the parameter.</param>
        /// <param name="dbType">One of the <see cref="System.Data.DbType"/> values. </param>
        /// <param name="value">The value of the parameter.</param>
        /// <returns>A reference to the added parameter.</returns>
        internal IDbDataParameter AddParameter(IDbCommand cmd, string paramName,
                                                DbType dbType, object value)
        {
            IDbDataParameter parameter = cmd.CreateParameter();
            parameter.ParameterName = CreateCollectionParameterName(paramName);
            parameter.DbType = dbType;
            parameter.Value = null == value ? DBNull.Value : value;
            cmd.Parameters.Add(parameter);
            return parameter;
        }

        /// <summary>
        /// Creates a .Net data provider specific name that is used by the 
        /// <see cref="AddParameter"/> method.
        /// </summary>
        /// <param name="baseParamName">The base name of the parameter.</param>
        /// <returns>The full data provider specific parameter name.</returns>
        protected abstract string CreateCollectionParameterName(string baseParamName);

        /// <summary>
        /// Gets <see cref="System.Data.IDbConnection"/> associated with this object.
        /// </summary>
        /// <value>A reference to the <see cref="System.Data.IDbConnection"/> object.</value>
        public IDbConnection Connection
        {
            get { return _connection; }
        }

        /// <summary>
        /// Gets an object that represents the <c>Category</c> table.
        /// </summary>
        public StoredProcedures StoredProcedures
        {
            get
            {
                if (null == _storedProcedures)
                    _storedProcedures = new StoredProcedures((MainDB)this);
                return _storedProcedures;
            }
        }

        /// <summary>
        /// Begins a new database transaction.
        /// </summary>
        /// <seealso cref="CommitTransaction"/>
        /// <seealso cref="RollbackTransaction"/>
        /// <returns>An object representing the new transaction.</returns>
        public IDbTransaction BeginTransaction()
        {
            CheckTransactionState(false);
            _transaction = _connection.BeginTransaction();
            return _transaction;
        }

        /// <summary>
        /// Begins a new database transaction with the specified 
        /// transaction isolation level.
        /// <seealso cref="CommitTransaction"/>
        /// <seealso cref="RollbackTransaction"/>
        /// </summary>
        /// <param name="isolationLevel">The transaction isolation level.</param>
        /// <returns>An object representing the new transaction.</returns>
        public IDbTransaction BeginTransaction(IsolationLevel isolationLevel)
        {
            CheckTransactionState(false);
            _transaction = _connection.BeginTransaction(isolationLevel);
            return _transaction;
        }

        /// <summary>
        /// Commits the current database transaction.
        /// <seealso cref="BeginTransaction"/>
        /// <seealso cref="RollbackTransaction"/>
        /// </summary>
        public void CommitTransaction()
        {
            CheckTransactionState(true);
            _transaction.Commit();
            _transaction = null;
        }

        /// <summary>
        /// Rolls back the current transaction from a pending state.
        /// <seealso cref="BeginTransaction"/>
        /// <seealso cref="CommitTransaction"/>
        /// </summary>
        public void RollbackTransaction()
        {
            CheckTransactionState(true);
            _transaction.Rollback();
            _transaction = null;
        }

        // Checks the state of the current transaction
        private void CheckTransactionState(bool mustBeOpen)
        {
            if (mustBeOpen)
            {
                if (null == _transaction)
                    throw new InvalidOperationException("Transaction is not open.");
            }
            else
            {
                if (null != _transaction)
                    throw new InvalidOperationException("Transaction is already open.");
            }
        }

        /// <summary>
        /// Creates and returns a new <see cref="System.Data.IDbCommand"/> object.
        /// </summary>
        /// <param name="sqlText">The text of the query.</param>
        /// <returns>An <see cref="System.Data.IDbCommand"/> object.</returns>
        internal IDbCommand CreateCommand(string sqlText)
        {
            return CreateCommand(sqlText, false);
        }

        /// <summary>
        /// Creates and returns a new <see cref="System.Data.IDbCommand"/> object.
        /// </summary>
        /// <param name="sqlText">The text of the query.</param>
        /// <param name="procedure">Specifies whether the sqlText parameter is 
        /// the name of a stored procedure.</param>
        /// <returns>An <see cref="System.Data.IDbCommand"/> object.</returns>
        internal IDbCommand CreateCommand(string sqlText, bool procedure)
        {
            IDbCommand cmd = _connection.CreateCommand();
            cmd.CommandText = sqlText;
            cmd.Transaction = _transaction;
            if (procedure)
                cmd.CommandType = CommandType.StoredProcedure;
            return cmd;
        }

        /// <summary>
        /// Rolls back any pending transactions and closes the DB connection.
        /// An application can call the <c>Close</c> method more than
        /// one time without generating an exception.
        /// </summary>
        public virtual void Close()
        {
            if (null != _connection)
                _connection.Close();
        }

        /// <summary>
        /// Rolls back any pending transactions and closes the DB connection.
        /// </summary>
        public virtual void Dispose()
        {
            Close();
            if (null != _connection)
                _connection.Dispose();
        }
        // All Property ung voi cac Field cua DBEntry

        public PropertyInfo[] GetAllProperties(DBEntry entry)
        {
            Type T = entry.GetType();
            PropertyInfo[] Properties = T.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public);
            return Properties;
        }

        // Chi nhung thuoc tinh ung voi cac Field cua DBEntry

        public PropertyInfo[] GetFilterProperties(DBEntry entry)
        {
            PropertyInfo[] ListObject = new PropertyInfo[entry.FieldCount];
            PropertyInfo[] Properties = GetAllProperties(entry);
            string[] ListFields = entry.GetListFields();
            string[] AllListNames = GetPropertyNames(Properties);
            try
            {
                for (int count = 0; count < ListFields.Length; count++)
                {
                    for (int i = 0; i < AllListNames.Length; i++)
                    {
                        if (ListFields[count].ToLower() == AllListNames[i].ToLower())
                        {
                            ListObject[count] = Properties[i];
                            break;
                        }
                        else
                        { continue; }
                    }
                }
            }
            catch (Exception EX)
            {
                Except.SetException(EX);
            }
            return ListObject;
        }


        // Lay mot gia tri cua 1 thuoc tinh nao do trong Entry

        public object GetPropertyValue(DBEntry entry, PropertyInfo Pinfo)
        {
            object Catchvalue = null;
            object[] Attributes = Pinfo.GetCustomAttributes(typeof(DBPropertyAttribute), false);
            if (Attributes.Length == 1)
            {
                try
                {
                    MethodInfo Mi = Pinfo.GetGetMethod(false);
                    object result = Mi.Invoke(entry, null);
                    Catchvalue = result;
                }
                catch (Exception EX)
                {
                    Except.SetException(EX);
                }
            }
            return Catchvalue;
        }


        // Lay mot ten tuy bien(AttributeName) cua 1 thuoc tinh nao do trong Entry

        public string GetPropertyName(PropertyInfo Pinfo)
        {
            string Catchname = "";
            object[] Attributes = Pinfo.GetCustomAttributes(typeof(DBPropertyAttribute), false);
            if (Attributes.Length == 1)
            {
                try
                {
                    DBPropertyAttribute Attrib = (DBPropertyAttribute)Attributes[0];
                    Catchname = Attrib.PropertyName;
                }
                catch (Exception EX)
                {
                    Except.SetException(EX);
                }
            }
            return Catchname;
        }


        // Lay mot 1 Kieu cua mot thuoc tinh nao do trong Entry

        public System.Data.OleDb.OleDbType GetPropertyType(PropertyInfo Pinfo)
        {
            System.Data.OleDb.OleDbType Catchtype = 0;
            object[] Attributes = Pinfo.GetCustomAttributes(typeof(DBPropertyAttribute), false);
            if (Attributes.Length == 1)
            {
                try
                {
                    DBPropertyAttribute Attrib = (DBPropertyAttribute)Attributes[0];
                    Catchtype = Attrib.PropertyType;
                }
                catch (Exception EX)
                {
                    Except.SetException(EX);
                }
            }
            return Catchtype;
        }


        // Lay length cua Field

        public int GetPropertyLength(PropertyInfo Pinfo)
        {
            int Catchint = 0;
            object[] Attributes = Pinfo.GetCustomAttributes(typeof(DBPropertyAttribute), false);
            if (Attributes.Length == 1)
            {
                try
                {
                    DBPropertyAttribute Attrib = (DBPropertyAttribute)Attributes[0];
                    Catchint = Attrib.Length;
                }
                catch (Exception EX)
                {
                    Except.SetException(EX);
                }
            }
            return Catchint; ;
        }


        // lay ra mot mang cac ten tuy bien(Attriubute) ung voi mang PropertyInfo

        public string[] GetPropertyNames(PropertyInfo[] PInfors)
        {
            int count = 0;
            string[] CatchLists = new string[PInfors.Length];
            try
            {
                foreach (PropertyInfo Pi in PInfors)
                {
                    CatchLists[count] = GetPropertyName(Pi);
                    count++;
                }
            }
            catch (Exception EX)
            {
                Except.SetException(EX);
            }
            return CatchLists;
        }


        // lay ra mot mang cac Kieu thuoc tinh ung voi mang PropertyInfo

        public System.Data.OleDb.OleDbType[] GetPropertyTypes(PropertyInfo[] PInfors)
        {
            int count = 0;
            System.Data.OleDb.OleDbType[] CatchLists = new System.Data.OleDb.OleDbType[PInfors.Length];
            try
            {
                foreach (PropertyInfo Pi in PInfors)
                {
                    CatchLists[count] = GetPropertyType(Pi);
                    count++;
                }
            }
            catch (Exception EX)
            {
                Except.SetException(EX);
            }
            return CatchLists;
        }


        // lay ra mot mang cac value ung voi mang PropertyInfo

        public object[] GetPropertyValues(DBEntry entry, PropertyInfo[] PInfors)
        {
            int count = 0;
            object[] CatchLists = new object[PInfors.Length];
            try
            {
                foreach (PropertyInfo Pi in PInfors)
                {
                    CatchLists[count] = GetPropertyValue(entry, Pi);
                    count++;
                }
            }

            catch (Exception EX)
            {
                Except.SetException(EX);
            }
            return CatchLists;

        }

    } // End of MainDB_Base class


    #region Attribute Classes

    [System.AttributeUsage(AttributeTargets.Class)]
    public class DBClassAttribute : System.Attribute
    {
        private string tablename;
        private string commandtext;
        private string parameter;
        public string TableName
        {
            get { return tablename; }
            set { tablename = value; }
        }
        public string CommandTextProc
        {
            get { return commandtext; }
            set { commandtext = value; }
        }
        public string Parameter
        {
            get { return parameter; }
            set { parameter = value; }
        }
        public DBClassAttribute()
        { tablename = ""; }
        public DBClassAttribute(string StrTableName)
        { tablename = StrTableName; }
    }
    [System.AttributeUsage(AttributeTargets.Property)]
    public class DBPropertyAttribute : System.Attribute
    {
        private string propertyname;
        private System.Data.OleDb.OleDbType propertytype;
        private int fieldlength;
        private string propertyparam;
        public string PropertyName
        {
            get { return propertyname; }
            set { propertyname = value; }
        }
        public System.Data.OleDb.OleDbType PropertyType
        {
            get { return propertytype; }
            set { propertytype = value; }
        }
        public int Length
        {
            get { return fieldlength; }
            set { fieldlength = value; }
        }
        public string PropertyParam
        {
            get { return propertyparam; }
            set { propertyparam = value; }
        }
        public DBPropertyAttribute()
        { propertyname = ""; }
        public DBPropertyAttribute(string InitName)
        { propertyname = InitName; }
    }

    #endregion

    public abstract class DBEntry
    {

        #region Private
        private int fieldcount = 0;
        private string[] ListFields;
        private string nameme;
        private bool all;
        private void InitEntry(string[] ListField)
        {
            fieldcount = ListField.Length;
            ListFields = new string[fieldcount];
            for (int count = 0; count < fieldcount; count++)
            { ListFields[count] = ListField[count]; }
        }

        #endregion

        #region Properties And Constructor
        public bool All
        {
            get { return all; }
            set { all = value; }
        }
        public int FieldCount
        {
            get { return fieldcount; }
            set { fieldcount = value; }
        }
        public string NameMe
        {
            get { return nameme; }
        }
        public DBEntry()
        {
            object[] Attris = this.GetType().GetCustomAttributes(typeof(DBClassAttribute), false);
            DBClassAttribute Att = (DBClassAttribute)Attris[0];
            this.nameme = Att.TableName;
        }
        #endregion

        #region Public Method
        public void SetListField()
        {
            this.all = true;
        }
        public void SetListField(params string[] ListFields)
        {
            this.all = false;
            InitEntry(ListFields);
        }
        public string[] GetListFields()
        {
            return this.ListFields;
        }

        #endregion

    }
}
